#include "Pch.h"
#include "Config.h"
#include "GLFont.h"
#include "glUtil.h"

GLFont::GLFont(const wxString &face, int point, wxWindow *window) :
face(face),
point(point),
error(0),
texture(0) {
	int mts = 0;
	glGetError();
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mts);
	// 256 by 256 required by ogl spec.
	if (glGetError() != GL_NO_ERROR || mts < 256) mts = 256;

	wxClientDC cdc(window);

	wxBitmap memBMP(128, 128);
    wxMemoryDC memDC(&cdc);
	memDC.SelectObject(memBMP);
	wxBrush oldBackground = memDC.GetBackground();

	//wxFont font(point, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, 0, face, wxFONTENCODING_DEFAULT);
	wxFont *font = wxFont::New(point, wxFONTFAMILY_SWISS, wxFONTFLAG_DEFAULT, face);

	wxFont oldFont = memDC.GetFont();
	memDC.SetFont(*font);

	int displayWidths[128];
	int displayX[128];
	int displayY[128];
	int height = 0;
	for (int i=32; i<128; i++) {
		wxChar c(i);
		wxString string(&c, 1);
		wxSize size = memDC.GetTextExtent(string);
		// Could draw text to get better prefix/suffix for better packing...
		// memDC.Clear();
		// memDC.DrawText(string, 2, 2);
		/*
		for (int x=0; x<size.x+4; x++) {
			for (int y=0; y<size.y+4; y++) {
				int w = 
			}
		}//*/

		if (size.y > height) height = size.y;

		chars[i].prefix = 1;
		chars[i].suffix = 2;
		chars[i].rasterStart = 0;
		chars[i].rasterWidth = size.x;

		displayWidths[i] = chars[i].rasterWidth + chars[i].prefix + chars[i].suffix;
	}
	this->height = height;

	int d;
	for (d = 256; d<=mts; d*=2) {
		int y = 0;
		int x = 0;
		int i;
		for (i=32; i<128; i++) {
			if (y + height > d) break;
			if (x + displayWidths[i] > d) {
				if (!x) break;
				x = 0;
				y += height;
			}

			chars[i].y = (y+0.5)/(float)d;
			chars[i].y2 = (y+height+0.5)/(float)d;
			chars[i].x = (x+0.5)/(float)d;
			chars[i].x2 = (x+displayWidths[i]+0.5)/(float)d;

			displayX[i] = x;
			displayY[i] = y;
			x += displayWidths[i];
		}
		if (i == 128) break;
	}
	if (d > mts) {
		error = 1;
		return;
	}
	wxBitmap memBMP2(d, d);
	memDC.SelectObject(memBMP2);
	// Have to do this here.  Doing it earlier doesn't work under Linux.
	memDC.SetBackground(wxBrush(wxColour(0,0,0)));
	// Seems to work earlier, but just in case.
	memDC.SetTextBackground(wxColour(0,0,0));
	memDC.SetTextForeground(wxColour(255,255,255));
	// Do it again, just in case.
	memDC.SetFont(*font);

	memDC.Clear();

	for (int i=32; i<128; i++) {
		wxChar c(i);
		wxString string(&c, 1);
		memDC.DrawText(string, displayX[i]+chars[i].prefix, displayY[i]);
	}
	int maxY = 0;

	int width = d;
	height = d;
	if (maxY < d/2) {
		height = d/2;
		for (int i=32; i<128; i++) {
			chars[i].y *= 2;
			chars[i].y2 *= 2;
		}
	}

	unsigned char *pixels = (unsigned char*) malloc(width*height * sizeof(unsigned char)*4);

	for (int x=0; x<width; x++) {
		for (int y=0; y<height; y++) {
			wxColour col;
			memDC.GetPixel(x, y, &col);
			unsigned char c = col.Red();
			pixels[4*(x + y*d)+0] = 255;
			pixels[4*(x + y*d)+1] = 255;
			pixels[4*(x + y*d)+2] = 255;
			pixels[4*(x + y*d)+3] = c;
			if (c && y > maxY) maxY = y;
			//pixels[4*(x + y*d)+3] = 255;
		}
	}
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
	free(pixels);

	for (int i=0; i<32; i++) {
		// Space
		chars[i] = chars[32];
	}

	memDC.SetFont(oldFont);
	memDC.SetBackground(oldBackground);
	delete font;
}

GLFont::~GLFont() {
	if (!error) {
		glFlush();
		glDeleteTextures(1, &texture);
	}
}

void GLFont::Begin() {
	mglColor3(white);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texture);
}

void GLFont::End() {
	glDisable(GL_TEXTURE_2D);
}

struct T2V3vertex {
	float t[2];
	float v[3];
};

void GLFont::Draw(const wxString &s, int x, int y, float z, unsigned int params) {
	Draw(s.mb_str(), x, y, z, params);
}

void GLFont::Draw(const char *s, int x, int y, float z, unsigned int params) {
	//glEnable(GL_TEXTURE_2D);
	//glBindTexture(GL_TEXTURE_2D, texture);
	//glBegin(GL_QUADS);

	int pxLen = 0;

	// Many more than I could possibly need, since currently don't bother
	// with linebreaks.
	T2V3vertex verts[512][4];
	int numVerts = 0;

	unsigned int len = strlen(s);
	if (len > 512) len = 512;

	if (params & DRAWFONT_HCENTER) {
		for (unsigned int i=0; i<len; i++) {
			wxChar c = s[i];
			if (c >= 128 || c < 32) c = 32;
			pxLen += chars[c].rasterWidth;
		}
		x -= pxLen/2;
	}
	if (params & DRAWFONT_HCENTER) {
		y -= height/2;
	}
	float yt = y - 0.5f;
	float yb = yt + height;

	for (unsigned int i=0; i<len; i++) {
		wxChar c = s[i];
		if (c >= 128 || c < 32) c = 32;
		float x1 = x - chars[c].prefix - 0.5;
		float x2 = x + chars[c].suffix+chars[c].rasterWidth - 0.5;

		verts[numVerts][0].t[0] = chars[c].x;
		verts[numVerts][0].t[1] = chars[c].y2;
		verts[numVerts][0].v[0] = x1;
		verts[numVerts][0].v[1] = yt;
		verts[numVerts][0].v[2] = z;

		verts[numVerts][1].t[0] = chars[c].x2;
		verts[numVerts][1].t[1] = chars[c].y2;
		verts[numVerts][1].v[0] = x2;
		verts[numVerts][1].v[1] = yt;
		verts[numVerts][1].v[2] = z;

		verts[numVerts][2].t[0] = chars[c].x2;
		verts[numVerts][2].t[1] = chars[c].y;
		verts[numVerts][2].v[0] = x2;
		verts[numVerts][2].v[1] = yb;
		verts[numVerts][2].v[2] = z;

		verts[numVerts][3].t[0] = chars[c].x;
		verts[numVerts][3].t[1] = chars[c].y;
		verts[numVerts][3].v[0] = x1;
		verts[numVerts][3].v[1] = yb;
		verts[numVerts][3].v[2] = z;

		/*glTexCoord2f(chars[c].x,  chars[c].y2);
		glVertex3i(x1, y, z);
		glTexCoord2f(chars[c].x2, chars[c].y2);
		glVertex3i(x2, y, z);
		glTexCoord2f(chars[c].x2, chars[c].y );
		glVertex3i(x2, y+height, z);
		glTexCoord2f(chars[c].x,  chars[c].y );
		glVertex3i(x1, y+height, z);
		//*/
		x += chars[c].rasterWidth;
		numVerts ++;
	}

	glInterleavedArrays(GL_T2F_V3F, 0, &verts[0][0]);
	glDrawArrays(GL_QUADS, 0, numVerts*4);
	//glDrawElements(GL_QUADS, numSplineIndices, GL_UNSIGNED_INT, splineIndices);

	//glEnd();
}
